﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Solver.Common
{
    public sealed class CircleArray<T> : ICollection<T>, IEnumerable<T>
    {
        
        Node<T> _head = null; // zacatek        
        Node<T> _tail = null; // konec
        Node<T> _first = null; // prvni uzel, need for reset
        int _count;

        readonly IEqualityComparer<T> comparer;

        public CircleArray()
        : this(null, EqualityComparer<T>.Default)
        {
        }
         
        /// <summary>
        /// Konstruktor <see cref="CircleArray"/>
        /// </summary>
        /// <param name="collection">Collection of objects that will be added to linked list</param>
        /// <param name="comparer">Comparer used for item comparison</param>
        public CircleArray(IEnumerable<T> collection, IEqualityComparer<T> comparer)
        {
            if (comparer == null)
                throw new ArgumentNullException("comparer");
            this.comparer = comparer;
            if (collection != null)
            {
                foreach (T item in collection)
                    this.AddLast(item);
            }
        }

        /// <summary>
        /// Konec
        /// </summary>
        public Node<T> Tail { get { return _tail; } }
        /// <summary>
        /// Zacatek
        /// </summary>
        public Node<T> Head { get { return _head; } }
        /// <summary>
        /// Prvni polozka vubec - urcenak resetovani 
        /// </summary>
        public Node<T> First { get { return _first; } }

        /// <summary>
        /// Pocet polozek v poli
        /// </summary>
        public int Count { get { return _count; } }

        /// <summary>
        /// Najde polozku podle indexu, prochazi od zacatku. vlastnost spoj seznamu
        /// </summary>
        /// <param name="index">index</param>
        public Node<T> this[int index]
        {
            get
            {
                if (index >= _count || index < 0)
                    throw new ArgumentOutOfRangeException("Out of Range array");
                else
                {
                    Node<T> node = this._head;
                    for (int i = 0; i < index; i++)
                        node = node.Next;
                    return node;
                }
            }
        }

        bool ICollection<T>.IsReadOnly
        {
            get { return false; }
        }

        /// <summary>
        /// pridat polozku ... na konec
        /// </summary>
        /// <param name="item"></param>
        void ICollection<T>.Add(T item)
        {
            this.AddLast(item);
        }

        public void Reset()
        {
            _head = _first;
        }

        // otoc kruh doleva
        public void RotateLeft()
        {
            _head = _head.Next;
        }
        // otoc kruh doprava
        public void RotateRight()
        {
            _head = _head.Previous;           
        }

        // posun se v poli
        public void Move()
        {
            _head = _head.Previous;
        }

        /// <summary>
        /// Pridat na konec
        /// </summary>
        /// <param name="item"></param>
        public void AddLast(T item)
        {
            if (item == null)
            {
                int a = 0;
            }

            if (_head == null)
                this.AddFirstItem(item);
            else
            {
                Node<T> newNode = new Node<T>(item);
                _tail.Next = newNode;
                newNode.Next = _head;
                newNode.Previous = _tail;
                _tail = newNode;
                _head.Previous = _tail;
            }
            _count++;
        }

        // pridej jako prvni polozku
        void AddFirstItem(T item)
        {
            _head = new Node<T>(item);
            _first = _head;
            _tail = _head;
            _head.Next = _tail;
            _head.Previous = _tail;
        }

        /// <summary>
        /// Najdi polozku
        /// </summary>
        /// <param name="item"></param>
        /// <returns>vraci uzel Node</returns>
        public Node<T> Find(T item)
        {
            Node<T> node = FindNode(_head, item);
            return node;
        }

        /// <summary>
        /// Odstrani prvni nalezenou polozku
        /// </summary>
        /// <param name="item">Item to be removed</param>
        /// <returns>TRUE if removed, else FALSE</returns>
        public bool Remove(T item)
        {
            // finding the first occurance of this item
            Node<T> nodeToRemove = this.Find(item);
            if (nodeToRemove != null)
                return this.RemoveNode(nodeToRemove);
            return false;
        }

        // Odstrani uzel
        bool RemoveNode(Node<T> remove_node)
        {
            Node<T> previous = remove_node.Previous;
            previous.Next = remove_node.Next;
            remove_node.Next.Previous = remove_node.Previous;

            // if this is head, we need to update the head reference
            if (_head == remove_node)
                _head = remove_node.Next;
            if (_first == remove_node)
                _first = remove_node.Next;
            else if (_tail == remove_node)
                _tail = _tail.Previous;

            _count--;
            return true;
        }


        /// <summary>
        /// Vycisti pole
        /// </summary>
        public void Clear()
        {
            _head = null;
            _tail = null;
            _first = null;
            _count = 0;
        }

        // find  node - najdi pole
        Node<T> FindNode(Node<T> node, T valueToCompare)
        {
            Node<T> result = null;
            if (comparer.Equals(node.Value, valueToCompare))
                result = node;
            else if (result == null && node.Next != _head)
                result = FindNode(node.Next, valueToCompare);
            return result;
        }

        /// <summary>
        /// Vrat enumerator
        /// </summary>
        /// <returns></returns>
        public IEnumerator<T> GetEnumerator()
        {
            Node<T> current = _head;
            if (current != null)
            {
                do
                {
                    yield return current.Value;
                    current = current.Next;
                } while (current != _head);
            }
        }


        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        /// <summary>
        /// Obshuje polozku ? ANO, NE
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool Contains(T item)
        {
            return Find(item) != null;
        }
        
        /// <summary>
        /// Kopirovani
        /// </summary>
        /// <param name="array"></param>
        /// <param name="arrayIndex"></param>
        public void CopyTo(T[] array, int arrayIndex)
        {
            if (array == null)
                throw new ArgumentNullException("array null");
            if (arrayIndex < 0 || arrayIndex > array.Length)
                throw new ArgumentOutOfRangeException("arrayIndex");

            Node<T> node = this._head;
            do
            {
                array[arrayIndex++] = node.Value;
                node = node.Next;
            } while (node != _head);
        }      
    }
}
